/*
 * BlockingQueue.java
 *
 * Created on 2008/06/08 +0900
 *
 * $Revision: 60 $
 * $Date: 2008-12-28 19:27:48 +0000 (Sun, 28 Dec 2008) $
 */

package org.gikolet.framework.util;

import java.util.Enumeration;
import java.util.NoSuchElementException;

/**
 * @author Tetsutaro (latest modification by $Author: tetsutaro.ss $)
 * @version $Revision: 60 $ $Date: 2008-12-28 19:27:48 +0000 (Sun, 28 Dec 2008) $
 */
public class BlockingQueue implements Queue {
    public static final int DEFAULT_CAPACITY = Deque.DEFAULT_CAPACITY;

    private Deque deque;

    public BlockingQueue() {
	this(BlockingQueue.DEFAULT_CAPACITY);
    }

    public BlockingQueue(int capacity) {
	this.deque = new Deque(capacity);
    }

    // Pushされた要素がPopされるまで待機
    public void blockingPushQueue(Object element) throws InterruptedException {
	Assert.isNotNull(element);

	synchronized (element) {
	    pushQueue(element);
	    element.wait();
	}
    }

    public synchronized Object blockingPopQueue() throws InterruptedException {
	Object resultElement;
	while ((resultElement = popQueue()) == null) {
	    wait();
	}
	return resultElement;
    }

    /**
     * @see org.gikolet.framework.util.Queue#getSize()
     */
    public synchronized int getSize() {
	return this.deque.getSize();
    }

    /**
     * @see org.gikolet.framework.util.Queue#isEmpty()
     */
    public synchronized boolean isEmpty() {
	return this.deque.isEmpty();
    }

    /**
     * @see org.gikolet.framework.util.Queue#peekQueue()
     */
    public synchronized Object peekQueue() {
	return this.deque.peekQueue();
    }

    /**
     * @see org.gikolet.framework.util.Queue#popQueue()
     */
    public synchronized Object popQueue() {
	Object obj = this.deque.popQueue();

	if (obj != null) {
	    synchronized (obj) {
		obj.notifyAll();
	    }
	}
	notifyAll();

	return obj;
    }

    /**
     * @see org.gikolet.framework.util.Queue#pushQueue(java.lang.Object)
     */
    public synchronized void pushQueue(Object element) {
	this.deque.pushQueue(element);

	notifyAll();
    }

    /**
     * @see org.gikolet.framework.util.Queue#elements()
     */
    public synchronized Enumeration elements() {
	return new Enumeration() {
	    private int index = 0;

	    /**
	     * @see java.util.Enumeration#hasMoreElements()
	     */
	    public boolean hasMoreElements() {
		synchronized (BlockingQueue.this) {
		    return this.index < getSize();
		}
	    }

	    /**
	     * @see java.util.Enumeration#nextElement()
	     */
	    public Object nextElement() {
		synchronized (BlockingQueue.this) {
		    if (!hasMoreElements()) {
			throw new NoSuchElementException();
		    }
		    return BlockingQueue.this.deque.element(this.index++);
		}
	    }
	};
    }
}
